#ifndef DataFifo_h__
#define DataFifo_h__
#include <stdlib.h>
#include <stdint.h>
#include <algorithm>
#include <assert.h>

template <typename T>
class DataFifo
{
public:
    DataFifo(uint16_t size);
    DataFifo();
    ~DataFifo();

    bool push_back(const T& data);
    uint16_t push_back(const void* pData, uint16_t size);

    bool pop_front() { return pop_front_count(1); }
    bool pop_front(T& data);
    bool pop_front(void* pData, uint16_t size);
    uint16_t pop_front_all(T* pData, uint16_t max_size);

    void clear(uint16_t size = 0xffff);


    bool empty() { return _out_idx == _in_idx; }	//is data empty
    bool full() { return (_in_idx + 1) % _max_size == _out_idx; }
    uint16_t size() {
        auto in = _in_idx, out = _out_idx;
        return in >= out ? (in - out) : (in + _max_size - out);
    }	//return size of the buffer
    uint16_t max() { return _max_size; }
    uint16_t remain_size() {
        auto in = _in_idx, out = _out_idx;
        return in >= out ? (_max_size - in + out - 1) : (out - in - 1);
    }//get avaliable empty block
    uint16_t reset_size(uint16_t size);


    uint16_t pop_front_count(const uint16_t _sz = 1);

    ///**
    // * @brief 擦除数据
    // * @param _pos 起始位置
    // * @param _sz 擦除个数
    // * @return 实际擦除数量
    //*/
    //uint16_t erase(uint16_t _pos, uint16_t _sz = 1);

    ///**
    // * @brief 插入数据
    // * @param _pos 插入位置
    // * @param _data 数据
    // * @return 实际插入数量
    //*/
    //uint16_t insert(uint16_t _pos, const T& _data);

    T& operator[](int idx)
    {
        assert(_max_size);
        return _buf[(idx + _out_idx) % _max_size];
    }

private:
    T* _buf;
    uint16_t _max_size;
    //volatile uint16_t _size;
    volatile uint16_t _out_idx;
    volatile uint16_t _in_idx;
};
//
//template <typename T>
//	uint16_t DataFifo<T>::insert(uint16_t _pos, const T& _data)
//	{
//		if (_size >= _max_size || _pos >= _size)
//			return 0;
//
//		uint16_t ed = _size;
//
//		++_size;
//		++_in_idx;
//		if (_in_idx >= _max_size)
//			_in_idx = 0;
//
//		while (ed != _pos)
//		{
//			(*this)[ed - 1] = (*this)[ed];
//			--ed;
//		}
//	
//		(*this)[ed] = _data;
//		return 1;
//	}

//template <typename T>
//	uint16_t DataFifo<T>::erase(uint16_t _pos, uint16_t _sz /*= 1*/)
//	{
//		if (_pos >= size())
//			return 0;
//		uint16_t ed = _pos + _sz;
//		if (ed < size())
//		{
//			while (ed < size())
//			{
//				(*this)[_pos] = (*this)[ed];
//				++_pos;
//				++ed;
//			}
//		}
//		else
//			_sz = _size - _pos;
//		_size -= _sz;
//		_in_idx = _in_idx >= _sz ? (_in_idx - _sz) : (_in_idx + _max_size - _sz);
//		return _sz;
//	}

template <typename T>
void DataFifo<T>::clear(uint16_t sz)
{
    if (sz >= size())
    {
        _out_idx = 0;
        _in_idx = 0;
    }
    else
    {
        auto out = _out_idx;
        out += sz;
        if (out >= _max_size)
            out -= _max_size;
        _out_idx = out;
    }
}

template <typename T>
DataFifo<T>::DataFifo()
    : _buf(NULL)
    , _max_size(0)
    , _out_idx(0)
    , _in_idx(0)
{

}

template <typename T>
uint16_t DataFifo<T>::reset_size(uint16_t sz)
{
    assert(sz < 65536);
    if (_buf) free(_buf);
    T* buf = (T*)malloc(sz * sizeof(T));
    if (buf == NULL)
        return _max_size;
    _buf = buf;
    _max_size = sz;
    _out_idx = 0;
    _in_idx = 0;
    return sz;
}

template <typename T>
uint16_t DataFifo<T>::pop_front_all(T* pData, uint16_t max_size)
{
    uint16_t cnt = size();
    cnt = cnt > max_size ? max_size : cnt;
    if (pop_front(pData, cnt))
        return cnt;
    else
        return 0;
}

template <typename T>
bool DataFifo<T>::pop_front(void* pData, uint16_t sz)
{
    if (size() < sz) return false;  //not enough data to get
    auto out = _out_idx;
    for (uint16_t i = 0; i < sz; i++)
    {
        ((T*)pData)[i] = _buf[out++];  //output data

        if (out >= _max_size)  //output index validate
            out = 0;
    }
    _out_idx = out;
    return true;
}

template <typename T>
bool DataFifo<T>::pop_front(T& data)
{
    if (empty())
        return false;
    auto out = _out_idx;
    data = _buf[out++];
    _out_idx = out >= _max_size ? 0 : out;
    return true;
}

template <typename T>
uint16_t DataFifo<T>::push_back(const void* pData, uint16_t sz)
{
    auto a = _in_idx;
    if (remain_size() < sz)
    {
        return 0;
    }

	for (uint16_t i = 0; i < sz; i++)
	{
		_buf[a++] = ((T*)pData)[i]; //input data
		if (a >= _max_size)
			a = 0;
	}
	_in_idx = a;
    return sz;
}

template <typename T>
bool DataFifo<T>::push_back(const T& data)
{
    if (full())	//buffer overflow
        return false;
    auto in = _in_idx;
    _buf[in++] = data;
    _in_idx = in >= _max_size ? 0 : in;
    return true;
}

template <typename T>
uint16_t DataFifo<T>::pop_front_count(uint16_t _sz)
{
    auto rtn = std::min(_sz, (uint16_t)size());;
    _out_idx = (_out_idx + rtn) % _max_size;
    return rtn;
}

template <typename T>
DataFifo<T>::DataFifo(uint16_t size)
    : _max_size(size)
    , _out_idx(0)
    , _in_idx(0)
{
    assert(size < 65536);
    _buf = (T*)malloc(size * sizeof(T));
}

template<typename T>
inline DataFifo<T>::~DataFifo()
{
    free(_buf);
}

#endif // DataFifo_h__
